#include "../stdafx.h"
#include "OpenGLDisplay.h"
#include "../geometry/SketchModel.h"
#include "../3rdparty/Triangulator.h"

COpenGLDisplay::COpenGLDisplay(CAnalyticDrawingView* pView)
{
	m_strokeManager.ClearStroke();
	m_pView = pView;
}

COpenGLDisplay::~COpenGLDisplay()
{

}

void COpenGLDisplay::InitRenderParas()
{
// 	glMatrixMode(GL_MODELVIEW);
// 	glLoadIdentity();

	//	miscs
	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_SMOOTH);

	//	materials
	m_material.SetMaterialParas(NULL, NULL, -1);
	m_lightManager.SetAmbientLight(NULL);

	m_lightManager.SetAmbientLight(NULL);
	m_lightManager.ClearAllLights();
	CLight light;
	light.SetLightIndex(0);
	light.SetLightParas(NULL, NULL, NULL);
	m_lightManager.AddNewLight(light);
}

void COpenGLDisplay::RenderScene(bool isWireModel)
{
	if (isWireModel)
	{
		RenderSceneWithWire();
	}
	else
	{
		RenderScenceWithLight();
	}
}

void COpenGLDisplay::RenderSceneWithWire()
{
	//m_lightManager.DisableAllLights();
	m_lightManager.DisableAllLights();
	glDisable(GL_DEPTH_TEST);
	//	do your own rendering

	//	render stroke first
	RenderStroke();
	//	render points
	RenderPoint();
	//	render scaffolds
	RenderScaffold();
	//	render line segment
	RenderLineSegment();
	//	render curves
	RenderCurve();
	//	render active vertex
	RenderActiveVertex();
}

void COpenGLDisplay::RenderScenceWithLight()
{
	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
	glEnable(GL_DEPTH_TEST);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glShadeModel(GL_SMOOTH);
	
	//glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);

	//	material
	m_material.SetMaterialParas(NULL, NULL, -1);

	//	light
	m_lightManager.SetAmbientLight(NULL);
	m_lightManager.GetLight(0)->SetLightParas(NULL, NULL, NULL);

	//	compute light position
	float glPosition[4], relPosition[4];
	m_lightManager.GetLight(0)->GetLightRelPosition(relPosition);
	CVertex3D glVertex, relVertex;
	relVertex.SetValue(relPosition[0] / relPosition[3], 
		relPosition[1] / relPosition[3], 
		relPosition[2] / relPosition[3]);
	glVertex = GetCamera()->GetGlobalPosition(relVertex);
	for (int i = 0; i < 3; i++)
		glPosition[i] = glVertex.m_data[i];
	glPosition[3] = 1.0f;
	m_lightManager.GetLight(0)->SetLightGlobalPosition(glPosition);
	m_lightManager.EnableAllLights();

	RenderSurfaces();
}

void COpenGLDisplay::RenderStroke()
{
 	//	compute 3d coords for stroke points
	CVertex2DIter itBegin = m_strokeManager.GetStrokeBeginIterator();
	CVertex2DIter itEnd = m_strokeManager.GetStrokeEndIterator();
	m_camera.SetCameraParas();
	glLineWidth(m_paint.GetLineWidth());
	float color[3];
	m_paint.GetLineColor(color);
	glColor3f(color[0], color[1], color[2]);
	glBegin(GL_LINE_STRIP);
	for (CVertex2DIter it = itBegin; it != itEnd; it++)
		DrawVertex(m_camera.UnProject(*it));
	glEnd();
}

void COpenGLDisplay::RenderPoint()
{
	CAnalyticDrawingView* pView = GetParentView();
	CAnalyticDrawingDoc* pDoc = pView->GetDocument();
	CSketchModel* pModel = pDoc->GetSketchModel();

	float color[3];
	m_paint.GetPointColor(color);
	glColor3f(color[0], color[1], color[2]);
	int count = pModel->GetVertexCount();
	CVertex v;
	glPointSize(m_paint.GetPointSize());
	for (int i = 0; i < count; i++)
	{
		v = *(pModel->GetVertex(i));
		glBegin(GL_POINTS);
			glVertex3f(v.m_v3D.m_data[0], v.m_v3D.m_data[1], v.m_v3D.m_data[2]);
		glEnd();
	}
}

void COpenGLDisplay::RenderLineSegment()
{
	CAnalyticDrawingView* pView = GetParentView();
	CAnalyticDrawingDoc* pDoc = pView->GetDocument();
	CSketchModel* pModel = pDoc->GetSketchModel();

	float color[3];
	m_paint.GetLineColor(color);
	glColor3f(color[0], color[1], color[2]);
	float width = m_paint.GetLineWidth();
	glLineWidth(width);
	int count = pModel->GetLineSegmentCount();
	CLineSegment l;
	CVertex3D start, end;
	for (int i = 0; i < count; i++)
	{
		l = pModel->GetLineSegment(i);
		start = l.m_start->m_v3D;
		end = l.m_end->m_v3D;
		glBegin(GL_LINES);
			glVertex3f(start.m_data[0], start.m_data[1], start.m_data[2]);
			glVertex3f(end.m_data[0], end.m_data[1], end.m_data[2]);
		glEnd();
	}
}

void COpenGLDisplay::RenderScaffold()
{
	CAnalyticDrawingView* pView = GetParentView();
	CAnalyticDrawingDoc* pDoc = pView->GetDocument();
	CSketchModel* pModel = pDoc->GetSketchModel();

	float color[3];
	m_paint.GetScaffoldColor(color);
	glColor3f(color[0], color[1], color[2]);
	int count = pModel->GetScaffoldCount();
	CScaffold s;
	CVertex3D start, end;
	CVector3D vUnit;
	for (int i = 0; i < count; i++)
	{
		s = pModel->GetScaffold(i);
		start = s.m_start->m_v3D;
		end = s.m_end->m_v3D;
		vUnit = end - start;
		//	compute start and end
		end = start + vUnit * (-10);
		start = start + vUnit * (10);
		glBegin(GL_LINES);
			glVertex3f(start.m_data[0], start.m_data[1], start.m_data[2]);
			glVertex3f(end.m_data[0], end.m_data[1], end.m_data[2]);
		glEnd();
	}
}

void COpenGLDisplay::RenderSurfaces()
{
	CAnalyticDrawingView* pView = GetParentView();
	CAnalyticDrawingDoc* pDoc = pView->GetDocument();
	CSketchModel* pModel = pDoc->GetSketchModel();
	//pModel->InferSurface(m_paint.GetCurveSegment());
	int surfaceCount = pModel->GetSurfaceCount();
	for (int i = 0; i < surfaceCount; i++)
		RenderSingleSurface(i);
}

void COpenGLDisplay::RenderSingleSurface(int surfaceIndex)
{
	CAnalyticDrawingView* pView = GetParentView();
	CAnalyticDrawingDoc* pDoc = pView->GetDocument();
	CSketchModel* pModel = pDoc->GetSketchModel();

	CSurface surface = pModel->GetSurface(surfaceIndex);
	int vertexCount = surface.m_vertices.size();
	CVector3D nx, ny, nz;
	ny = surface.m_normal;
	nx = surface.m_vertices[1] - surface.m_vertices[0];
	nz = Cross(nx, ny);
	nx.Normalize(); ny.Normalize(); nz.Normalize();
	Triangulator *t = createTriangulator();
	CVertex3D p;
	for (int i = 0; i < vertexCount; i++)
	{
		p = surface.m_vertices[i];
		t->addPoint(p * nx, p * ny, p * nz);
	}
	unsigned int count;
	unsigned int* indices = t->triangulate(count);

	//	render triangles
	CVertex3D p2, p3;
	CVector3D v1, v2, n;
	for (int i = 0; i < (int)count; i++)
	{
		p = surface.m_vertices[(int)indices[3 * i]];
		p2 = surface.m_vertices[(int)indices[3 * i + 1]];
		p3 = surface.m_vertices[(int)indices[3 * i + 2]];
		v1 = p2 - p;
		v2 = p3 - p2;
		n = Cross(v1, v2);
		//n.Clip();
		n.Normalize();
		//glEnable(GL_NORMALIZE);
		glBegin(GL_TRIANGLES);
			glNormal3f(n.m_data[0], n.m_data[1], n.m_data[2]);
			glVertex3f(p.m_data[0], p.m_data[1], p.m_data[2]);
			glNormal3f(n.m_data[0], n.m_data[1], n.m_data[2]);
			glVertex3f(p2.m_data[0], p2.m_data[1], p2.m_data[2]);
			glNormal3f(n.m_data[0], n.m_data[1], n.m_data[2]);
			glVertex3f(p3.m_data[0], p3.m_data[1], p3.m_data[2]);
		glEnd();
	}
	releaseTriangulator(t);
}

void COpenGLDisplay::RenderCurve()
{
	CAnalyticDrawingView* pView = GetParentView();
	CAnalyticDrawingDoc* pDoc = pView->GetDocument();
	CSketchModel* pModel = pDoc->GetSketchModel();

	int count = pModel->GetCurveCount();
	CCurve curve;
	GLfloat controlPoints[3][3];

	float width = m_paint.GetLineWidth();
	float color[3];
	m_paint.GetLineColor(color);
	glColor3f(color[0], color[1], color[2]);
	glLineWidth(width);
	int segment = m_paint.GetCurveSegment();
	for (int i = 0; i < count; i++)
	{
		curve = pModel->GetCurve(i);
		for (int j = 0; j < 3; j++)
		{
			controlPoints[0][j] = curve.m_start->m_v3D.m_data[j];
			controlPoints[1][j] = curve.m_mid->m_v3D.m_data[j];
			controlPoints[2][j] = curve.m_end->m_v3D.m_data[j];
		}
		glMap1f(GL_MAP1_VERTEX_3,
			0.0f, 1.0f,
			3,
			3,
			&controlPoints[0][0]);
		glEnable(GL_MAP1_VERTEX_3);
		
		glMapGrid1f(segment, 0.0f, 1.0f);
		glEvalMesh1(GL_LINE, 0, segment);
	}
}

void COpenGLDisplay::RenderActiveVertex()
{
	CAnalyticDrawingView* pView = GetParentView();
	CAnalyticDrawingDoc* pDoc = pView->GetDocument();
	CSketchModel* pModel = pDoc->GetSketchModel();

	float color[3];
	m_paint.GetActiveVertexColor(color);
	glColor3f(color[0], color[1], color[2]);
	int count = pModel->GetVertexCount();
	glPointSize(m_paint.GetPointSize());
	CVertex *v = pModel->GetActiveVertex();
	if (v)
	{
		glBegin(GL_POINTS);
			glVertex3f(v->m_v3D.m_data[0], v->m_v3D.m_data[1], v->m_v3D.m_data[2]);
		glEnd();
	}
}